#ifndef __STR_H__
  #define __STR_H__

#include <algorithm>
#include <string>
#include <list>

#include "arg.h"
#include "converter.h"

using namespace std;

//! Helper class for std::string

/*!
This class helps to deal with std::strings in a comfortable way.

Implementation date: 2008

Usage:

\code
bool b = str::begins_with("This is the input string!", "thus"); // will return false

string s = "This one costs %1$ which is %2% percent higher/lower.";
str::format(s, arguments<double>(12.19, 20)); // s will be: This one costs 12.19$ which is 20% percent higher/lower.
\endcode
*/

class str {
public:
  //! returns true if the source string begins with the search_string
  static bool begins_with(const string &src, const string &search_string, bool case_sensitive = true);

  //! returns true if the source string ends with the search_string
  static bool ends_with(const string &src, const string &search_string, bool case_sensitive = true);

  //! returns true if the source string contains the search_string
  static bool contains(const string &src, const string &search_string, bool case_sensitive = true);

  //! returns true if the source string is equal to the search_string
  static bool equals(const string &src, const string &search_string, bool case_sensitive = true);

  //! returns true if every single character in the source string is a digit (0-9)
  static bool is_digit(const string &src);

  //! replaces all occurrences of search_string with replace_with
  static void replace_all(string &src, const string &search_string, const string &replace_with);

  //! removes all occurrences of search_string
  static void remove_all(string &src, const string &search_string);

  //! trims the source string using the given trim characters
  static void trim(string &src, const string &trim_characters = " \r\n\t");

  //! trims the source string using the given trim characters from left
  static void trim_left(string &src, const string &trim_characters = " \r\n\t");

  //! trims the source string using the given trim characters from right
  static void trim_right(string &src, const string &trim_characters = " \r\n\t");

  //! returns the left count characters of the source string. if the count is larger than the length of the source string, then the source string will not be changed in anyway
  static void left(string &src, unsigned int count);

  //! returns the right count characters of the source string. if the count is larger than the length of the source string, then the source string will not be changed in anyway
  static void right(string &src, unsigned int count);

  //! returns the middle count characters of the source string starting at start_index. if the start_index is larger than the length of the source string, then the source string will not be changed in anyway
  static void mid(string &src, unsigned int start_index, unsigned int count);

  //! separates the given string according to the given pattern and saves the separated strings to the output
  static void split(const string &src, const string &pattern, list<string> &output);

  //! returns the number of occurrences of the search_string within the source string
  static unsigned int count(const string &src, const string &search_string);

  //! converts the source string to its uppercase format
  static void to_upper(string &src);

  //! converts the source string to its lowercase format
  static void to_lower(string &src);

  //! prepenst c character(s) to the source string until the total_length is reached
  static void prepend(string &src, char c, unsigned short total_length);

  //! formats the source string with the given arg
  /*!
  use %1, %2, ... for parameters (one-based)
  */
  template <class T, class U, class V, class W, class X, class Y, class Z>
  static void format(string &src, const arg<T, U, V, W, X, Y, Z> &a) {
    const unsigned short items = a.size();
    string str_cnv;
    size_t i = 0;

    while (true) {
      i = src.find('%', i);
      if (i != string::npos && (i + 1) < src.length()) {
        const char digit = src[i + 1];
        if (digit == '1' && items >= 1)
          converter::to_string(a.get_value1(), str_cnv);
        else if (digit == '2' && items >= 2)
          converter::to_string(a.get_value2(), str_cnv);
        else if (digit == '3' && items >= 3)
          converter::to_string(a.get_value3(), str_cnv);
        else if (digit == '4' && items >= 4)
          converter::to_string(a.get_value4(), str_cnv);
        else if (digit == '5' && items >= 5)
          converter::to_string(a.get_value5(), str_cnv);
        else if (digit == '6' && items >= 6)
          converter::to_string(a.get_value6(), str_cnv);
        else if (digit == '7' && items >= 7)
          converter::to_string(a.get_value7(), str_cnv);
        else {
          ++i;
          continue;
        } // else

        src.replace(i, 2, str_cnv);
        i += str_cnv.length();
      } else
        break;
    } // while
  }
};

#endif
